SECURITY: Prevent tokens in jsonp mode
authorBrad Jorsch <bjorsch@wikimedia.org>
Thu, 29 Aug 2013 16:45:30 +0000 (09:45 -0700)
committercsteipp <csteipp@wikimedia.org>
Tue, 3 Sep 2013 22:04:47 +0000 (15:04 -0700)
Add checks to token-returning functions to prevent returning tokens in
jsonp mode. This affects action=tokens, action=login,
action=createaccount, and action=query&list=deletedrevs.

Also, remove the "gettoken" parameter to action=block and
action=unblock, which has been deprecated since 1.20.

Bug: 49090
Change-Id: Ibeaa5c72d8084585092b15935a3f5709104bf7f7

RELEASE-NOTES-1.22
includes/api/ApiBlock.php
includes/api/ApiCreateAccount.php
includes/api/ApiLogin.php
includes/api/ApiMain.php
includes/api/ApiQueryDeletedrevs.php
includes/api/ApiTokens.php
includes/api/ApiUnblock.php
tests/phpunit/includes/api/ApiBlockTest.php

index 859d6c9..f89cbe2 100644 (file)
@@ -338,6 +338,9 @@ production.
   version of the title.
 * (bug 52538) action=edit will now use empty text instead of the contents
   of section 0 when passed prependtext or appendtext with section=new.
+* Support for the 'gettoken' parameter to action=block and action=unblock,
+  deprecated since 1.20, has been removed.
+* (bug 49090) Token-getting functions will fail when using jsonp callbacks.
 
 === Languages updated in 1.22===
 
index ab0a7e9..975153a 100644 (file)
@@ -42,12 +42,6 @@ class ApiBlock extends ApiBase {
                $user = $this->getUser();
                $params = $this->extractRequestParams();
 
-               if ( $params['gettoken'] ) {
-                       $res['blocktoken'] = $user->getEditToken();
-                       $this->getResult()->addValue( null, $this->getModuleName(), $res );
-                       return;
-               }
-
                if ( !$user->isAllowed( 'block' ) ) {
                        $this->dieUsageMsg( 'cantblock' );
                }
@@ -156,10 +150,6 @@ class ApiBlock extends ApiBase {
                                ApiBase::PARAM_REQUIRED => true
                        ),
                        'token' => null,
-                       'gettoken' => array(
-                               ApiBase::PARAM_DFLT => false,
-                               ApiBase::PARAM_DEPRECATED => true,
-                       ),
                        'expiry' => 'never',
                        'reason' => '',
                        'anononly' => false,
@@ -177,7 +167,6 @@ class ApiBlock extends ApiBase {
                return array(
                        'user' => 'Username, IP address or IP range you want to block',
                        'token' => 'A block token previously obtained through prop=info',
-                       'gettoken' => 'If set, a block token will be returned, and no other action will be taken',
                        'expiry' => 'Relative expiry time, e.g. \'5 months\' or \'2 weeks\'. If set to \'infinite\', \'indefinite\' or \'never\', the block will never expire.',
                        'reason' => 'Reason for block',
                        'anononly' => 'Block anonymous users only (i.e. disable anonymous edits for this IP)',
@@ -194,10 +183,6 @@ class ApiBlock extends ApiBase {
        public function getResultProperties() {
                return array(
                        '' => array(
-                               'blocktoken' => array(
-                                       ApiBase::PROP_TYPE => 'string',
-                                       ApiBase::PROP_NULLABLE => true
-                               ),
                                'user' => array(
                                        ApiBase::PROP_TYPE => 'string',
                                        ApiBase::PROP_NULLABLE => true
index 201634d..0e752c5 100644 (file)
  */
 class ApiCreateAccount extends ApiBase {
        public function execute() {
+               // If we're in JSON callback mode, no tokens can be obtained
+               if ( !is_null( $this->getMain()->getRequest()->getVal( 'callback' ) ) ) {
+                       $this->dieUsage( 'Cannot create account when using a callback', 'aborted' );
+               }
 
                // $loginForm->addNewaccountInternal will throw exceptions
                // if wiki is read only (already handled by api), user is blocked or does not have rights.
index b936d3b..b51d441 100644 (file)
@@ -46,6 +46,15 @@ class ApiLogin extends ApiBase {
         * is reached. The expiry is $this->mLoginThrottle.
         */
        public function execute() {
+               // If we're in JSON callback mode, no tokens can be obtained
+               if ( !is_null( $this->getMain()->getRequest()->getVal( 'callback' ) ) ) {
+                       $this->getResult()->addValue( null, 'login', array(
+                               'result' => 'Aborted',
+                               'reason' => 'Cannot log in when using a callback',
+                       ) );
+                       return;
+               }
+
                $params = $this->extractRequestParams();
 
                $result = array();
index 4dd1713..c10d85c 100644 (file)
@@ -714,15 +714,9 @@ class ApiMain extends ApiBase {
                }
                $moduleParams = $module->extractRequestParams();
 
-               // Die if token required, but not provided (unless there is a gettoken parameter)
-               if ( isset( $moduleParams['gettoken'] ) ) {
-                       $gettoken = $moduleParams['gettoken'];
-               } else {
-                       $gettoken = false;
-               }
-
+               // Die if token required, but not provided
                $salt = $module->getTokenSalt();
-               if ( $salt !== false && !$gettoken ) {
+               if ( $salt !== false ) {
                        if ( !isset( $moduleParams['token'] ) ) {
                                $this->dieUsageMsg( array( 'missingparam', 'token' ) );
                        } else {
index 690d0e6..8273313 100644 (file)
@@ -57,6 +57,11 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
                $fld_content = isset( $prop['content'] );
                $fld_token = isset( $prop['token'] );
 
+               // If we're in JSON callback mode, no tokens can be obtained
+               if ( !is_null( $this->getMain()->getRequest()->getVal( 'callback' ) ) ) {
+                       $fld_token = false;
+               }
+
                $result = $this->getResult();
                $pageSet = $this->getPageSet();
                $titles = $pageSet->getTitles();
index 7080f54..d220a5e 100644 (file)
@@ -48,6 +48,11 @@ class ApiTokens extends ApiBase {
        }
 
        private function getTokenTypes() {
+               // If we're in JSON callback mode, no tokens can be obtained
+               if ( !is_null( $this->getMain()->getRequest()->getVal( 'callback' ) ) ) {
+                       return array();
+               }
+
                static $types = null;
                if ( $types ) {
                        return $types;
index 55e7331..6a739a2 100644 (file)
@@ -39,12 +39,6 @@ class ApiUnblock extends ApiBase {
                $user = $this->getUser();
                $params = $this->extractRequestParams();
 
-               if ( $params['gettoken'] ) {
-                       $res['unblocktoken'] = $user->getEditToken();
-                       $this->getResult()->addValue( null, $this->getModuleName(), $res );
-                       return;
-               }
-
                if ( is_null( $params['id'] ) && is_null( $params['user'] ) ) {
                        $this->dieUsageMsg( 'unblock-notarget' );
                }
@@ -96,10 +90,6 @@ class ApiUnblock extends ApiBase {
                        ),
                        'user' => null,
                        'token' => null,
-                       'gettoken' => array(
-                               ApiBase::PARAM_DFLT => false,
-                               ApiBase::PARAM_DEPRECATED => true,
-                       ),
                        'reason' => '',
                );
        }
@@ -110,7 +100,6 @@ class ApiUnblock extends ApiBase {
                        'id' => "ID of the block you want to unblock (obtained through list=blocks). Cannot be used together with {$p}user",
                        'user' => "Username, IP address or IP range you want to unblock. Cannot be used together with {$p}id",
                        'token' => "An unblock token previously obtained through prop=info",
-                       'gettoken' => 'If set, an unblock token will be returned, and no other action will be taken',
                        'reason' => 'Reason for unblock',
                );
        }
@@ -118,10 +107,6 @@ class ApiUnblock extends ApiBase {
        public function getResultProperties() {
                return array(
                        '' => array(
-                               'unblocktoken' => array(
-                                       ApiBase::PROP_TYPE => 'string',
-                                       ApiBase::PROP_NULLABLE => true
-                               ),
                                'id' => array(
                                        ApiBase::PROP_TYPE => 'integer',
                                        ApiBase::PROP_NULLABLE => true
index 3c18968..d0eb18a 100644 (file)
@@ -62,22 +62,6 @@ class ApiBlockTest extends ApiTestCase {
                $this->assertEquals( 'infinity', $block->mExpiry );
        }
 
-       /**
-        * @dataProvider provideBlockUnblockAction
-        */
-       function testGetTokenUsingABlockingAction( $action ) {
-               $data = $this->doApiRequest(
-                       array(
-                               'action' => $action,
-                               'user' => 'UTApiBlockee',
-                               'gettoken' => '' ),
-                       null,
-                       false,
-                       self::$users['sysop']->user
-               );
-               $this->assertEquals( 34, strlen( $data[0][$action]["{$action}token"] ) );
-       }
-
        /**
         * Attempting to block without a token should give a UsageException with
         * error message: